package com.youlu.campus.service.rank.impl;

import com.alibaba.fastjson.JSON;
import com.youlu.campus.base.community.RankingList;

import com.youlu.campus.entity.ActivityInfo;
import com.youlu.campus.entity.ActivityTaskInfo;
import com.youlu.campus.entity.SystemNickNameAndHeaderImage;
import com.youlu.campus.entity.UniversityInfo;
import com.youlu.campus.entity.UserPO;
import com.youlu.campus.entity.UserParticipateActivityInfo;
import com.youlu.campus.entity.VO.RankingListQueryVO;
import com.youlu.campus.entity.VO.req.RankingListResVO;
import com.youlu.campus.service.cache.CacheActivityService;
import com.youlu.campus.service.rank.RankBaseService;
import com.youlu.campus.service.rank.UserRankListService;
import com.youlu.campus.service.ranking.UserRankBlackListService;
import com.youlu.campus.service.visitor.VisitorUserService;
import com.youlu.campus.service.wechat.SystemNickNameAndHeaderImageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserRankListServiceImpl implements UserRankListService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RankBaseService rankBaseService;
    @Autowired
    private CacheActivityService cacheActivityService;
    @Autowired
    private VisitorUserService visitorUserService;
    @Autowired
    private UserRankBlackListService userRankBlackListService;
    @Autowired
    private SystemNickNameAndHeaderImageService systemNickNameAndHeaderImageService;

    /**
     * 全国榜Key
     */
    private String ALL_RANK_COUNTRY_KEY = "com.luyou.rank.all.activityId:%s.taskId:%s";
    /**
     * 省榜Key
     */
    private String ALL_RANK_PROVINCE_KEY = "com.luyou.rank.province.activityId:%s.taskId:%s.provinvce:%s";
    /**
     * 用户Key
     */
    private String ALL_RANK_USER_KEY = "com.luyou.rank.user.userId:%s.platform:%s";

    /**
     * 获取用户总榜
     *
     * @param req
     * @param userPO
     * @param taskInfo
     * @return
     */
    @Override
    public RankingListResVO getUserAllRankList(RankingListQueryVO req, UserPO userPO, ActivityTaskInfo taskInfo) {
        RankingListResVO rankingListResVO = new RankingListResVO();
        Long startTime = System.currentTimeMillis();
        List<String> rankBlackList = userRankBlackListService.findUserIds(req.getActivityId());
        try {
            if (Objects.isNull(req.getPageSize())) {
                req.setPageSize(10);
            }
            if (Objects.isNull(req.getPage())) {
                req.setPage(1);
            }
            String key = "";
            String province = "";
            if (StringUtils.isNotBlank(req.getProvince())) {
                province = visitorUserService.getUserProvince(req.getActivityId(), userPO.getId());
                if (StringUtils.isBlank(province)) {
                    province = mongoTemplate.findById(userPO.getId(), UserPO.class).getProvince();
                    log.error("getUserAllRankList 获取省份失败 userId:{},activityId:{},province:{}", userPO.getId(), req.getActivityId(), province);
                }
            }
            log.info(":>>> 用户:{} 所在省:{}", userPO.getId(), province);
            if (StringUtils.isBlank(province)) {
                key = this.getAllRankCountryKey(req.getActivityId(), req.getTaskId());
            } else {
                key = getAllRankProvinceKey(req.getActivityId(), req.getTaskId(), province);
            }
            Long count = rankBaseService.getRankTotal(key);
            //return (x < y) ? -1 : ((x == y) ? 0 : 1);
            if (count.intValue() > taskInfo.getRangNum()) {
                count = Long.valueOf(taskInfo.getRangNum() + "");
                log.info(":>>> 活动:{} 任务:{} 排行榜数量大于任务配置上限:{}", req.getActivityId(), req.getTaskId(), count);
            }
            if (count.intValue() < req.getPageSize()) {
                req.setPageSize(count.intValue());
            }
            if (req.getPageSize() == 0) {
                req.setPageSize(1);
            }
            Integer totalPages = 0;
            if (count.intValue() == 0) {
                totalPages = 0;
            } else {
                if (count.intValue() % req.getPageSize() == 0) {
                    totalPages = count.intValue() / req.getPageSize();
                } else {
                    totalPages = count.intValue() / req.getPageSize() + 1;
                }
            }
            log.info(":>>> 用户排行榜数量:{},总页数:{},每页大小:{},当前页:{}", count, totalPages, req.getPageSize(), req.getPage());
            boolean in = false;
            List<RankingList> rankingLists = new ArrayList<>();
            if (req.getPage().intValue() <= totalPages.intValue()) {
                int end = (req.getPage() - 1) * req.getPageSize() + req.getPageSize();
                if (end > 1) {
                    end = end - 1;
                }
                Set<ZSetOperations.TypedTuple<String>> datas = rankBaseService.getRankDatas(key, (req.getPage() - 1) * req.getPageSize(), (req.getPage() - 1) * req.getPageSize() + req.getPageSize() - 1);
                if (Objects.isNull(datas)) {
                    datas = new HashSet<ZSetOperations.TypedTuple<String>>();
                }
                ActivityInfo activityInfo = cacheActivityService.findById(req.getActivityId());
                int order = 1;
                int uOrder = 1001;
                int orderNN = (req.getPage() - 1) * req.getPageSize() + 1;
                List<String> userIds = new ArrayList<>();
                for (ZSetOperations.TypedTuple<String> d : datas) {
                    if (StringUtils.isNotBlank(d.getValue())) {
                        userIds.add(d.getValue());
                    }
                }
                for (ZSetOperations.TypedTuple<String> d : datas) {
                    // 在排行榜黑名单里不显示
                    if (rankBlackList.contains(d.getValue())) {
                        continue;
                    }
                    if (req.getUserId().equals(d.getValue())) {
                        in = true;
                        uOrder = order;
                    }
                    UserPO user = this.get(d.getValue(), req.getPlatform());
                    RankingList rankingList = new RankingList();
                    if (Objects.nonNull(user)) {
                        boolean nickName = false;
                        boolean avatarUrl = false;
                        if (StringUtils.isNotBlank(user.getRankAvatarUrl())) {
                            rankingList.setImageUrl(user.getRankAvatarUrl());
                            avatarUrl = true;
                        } else {
                            if (StringUtils.isNotBlank(user.getAvatarUrl())) {
                                rankingList.setImageUrl(user.getAvatarUrl());
                                avatarUrl = true;
                            }

                        }
                        if (StringUtils.isNotBlank(user.getRankNickName())) {
                            rankingList.setName(user.getRankNickName());
                            nickName = true;
                        } else {
                            if (StringUtils.isNotBlank(user.getNickName())) {
                                nickName = true;
                                rankingList.setName(user.getNickName());
                            }

                        }
                        if (!nickName || !avatarUrl) {
                            log.info(":>>> 活动:{},用户:{} 昵称或头像为空,使用系统默认头像", req.getActivityId(), d.getValue());
                            SystemNickNameAndHeaderImage defaultSys = systemNickNameAndHeaderImageService.getRandom();
                            if (Objects.nonNull(defaultSys)) {
                                if (!nickName) {
                                    rankingList.setName(defaultSys.getNickName());
                                }
                                if (!avatarUrl) {
                                    rankingList.setImageUrl(defaultSys.getAvatarUrl());
                                }
                            }
                        }
                    }
//                this.setNickNameAndImage(userPO1, rankingList);
                    if (Objects.nonNull(d.getScore())) {
                        BigDecimal b = new BigDecimal(d.getScore());
                        Double q = b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
                        rankingList.setQuantity(q);
                    }
                    rankingList.setRank(activityInfo.getRewardValueUnitDesc());
                    rankingList.setOrderNo(orderNN);
                    rankingLists.add(rankingList);
                    order++;
                    orderNN++;
                }
                rankingListResVO.setDatas(rankingLists);
                rankingListResVO.setInRanking(in);
                Long o = rankBaseService.getSingleRankOrder(key, userPO.getId());
                Double q = rankBaseService.getSingleRankScore(key, userPO.getId());
                if (Objects.isNull(o)) {
                    rankingListResVO.setOrderNo(0);
                } else {
                    rankingListResVO.setOrderNo(o.intValue());
                }
                if (Objects.isNull(1)) {
                    rankingListResVO.setQuantity(0.0D);
                } else {
                    rankingListResVO.setQuantity(q);
                }
            } else {
                rankingListResVO.setDatas(rankingLists);
            }
//            this.setRankName(rankingListResVO, userPO);
//            this.setDefaultHead(rankingListResVO, req.getUserPO().getAvatarUrl());
            rankingListResVO.setInRanking(in);
            rankingListResVO.setDetail(taskInfo.getRankingListContent());
            rankingListResVO.setPage(req.getPage());
            rankingListResVO.setPageSize(req.getPageSize());
            rankingListResVO.setTotalPages(count.intValue() / req.getPageSize() + 1);
            if (req.getPage() < (count.intValue() / req.getPageSize() + 1)) {
                rankingListResVO.setHasNext(true);
            }
            log.info(":>>> 全国排行榜耗时:{} 毫秒,总页数:{},当前页:{},目前总数据:{}", System.currentTimeMillis() - startTime, count.intValue() / req.getPageSize() + 1, req.getPage(), rankingListResVO.getDatas() == null ? 0 : rankingListResVO.getDatas().size());
        } catch (Exception e) {
            log.error(":>>> 获取排行榜错误:{}", e);
        }
        return rankingListResVO;
    }

    @Override
    public String getAllRankCountryKey(String activityId, String taskId) {
        return String.format(ALL_RANK_COUNTRY_KEY, activityId, taskId);
    }

    @Override
    public String getAllRankProvinceKey(String activityId, String taskId, String province) {
        return String.format(ALL_RANK_PROVINCE_KEY, activityId, taskId, province);
    }

    @Async
    @Override
    public void updateRankCountry(String activityId, String taskId, String userId, Double q, Double qTotal) {
        log.info(":>>> 开始更新用户:{},活动:{},全国排行榜缓存:{}", userId, activityId, q);
        try {
            String key = this.getAllRankCountryKey(activityId, taskId);
            Long total = rankBaseService.getRankTotal(key);
            if (Objects.isNull(total) || total.compareTo(1L) != 1) {
                log.info(":>>> 全国榜:{} 排行榜为空:{} 直接添加:{},{}", key, total, userId, qTotal);
                rankBaseService.addSingleRank(key, userId, qTotal);
                return;
            }
            ZSetOperations.TypedTuple<String> max = rankBaseService.getMax(key);
            ZSetOperations.TypedTuple<String> min = rankBaseService.getMin(key);
            if (Objects.isNull(max) || Objects.isNull(min)) {
                log.info(":>>> 活动:{} 全国榜最大值或最小值为空 ");
                rankBaseService.addSingleRank(key, userId, qTotal);
                return;
            }
            Double m = max.getScore();
            Double n = min.getScore();
            log.info(":>>> 活动:{} 全国榜最大值:{} 最小值:{} ", activityId, m, n);
            rankBaseService.remove(key, userId);
            rankBaseService.addSingleRank(key, userId, qTotal);
            Double after = rankBaseService.getSingleRankScore(key, userId);
            log.info(":>>> 结束更新用户:{},活动:{},全国排行榜缓存:{}", userId, activityId, after);
        } catch (Exception e) {
            log.error(":>>> 更新用户全国排行榜缓存错误:{}", e);
        }
    }

    /**
     * 删除缓存
     *
     * @param activityId
     * @param taskId
     * @param userId
     */
    @Override
    public void deleteRankCountry(String activityId, String taskId, String userId) {
        String key = this.getAllRankCountryKey(activityId, taskId);
        rankBaseService.remove(key, userId);
    }

    @Override
    public void updateRankProvince(String activityId, String taskId, String userId, String province, Double qTotal) {
        log.info("updateRankProvince 开始更新 用户:{},活动:{},省:{} 贡献值:{}", userId, activityId, province, qTotal);
        try {
            String key = this.getAllRankProvinceKey(activityId, taskId, province);
            Long total = rankBaseService.getRankTotal(key);
            if (Objects.isNull(total) || total.compareTo(1L) != 1) {
                log.info("updateRankProvince 省榜:{} 排行榜为空:{} 直接添加:{},{}", key, total, userId, qTotal);
                rankBaseService.addSingleRank(key, userId, qTotal);
                return;
            }
            ZSetOperations.TypedTuple<String> max = rankBaseService.getMax(key);
            ZSetOperations.TypedTuple<String> min = rankBaseService.getMin(key);
            if (Objects.isNull(max) || Objects.isNull(min)) {
                log.info("updateRankProvince 活动:{} 省榜最大值或最小值为空 ");
                rankBaseService.addSingleRank(key, userId, qTotal);
                return;
            }
            Double m = max.getScore();
            Double n = min.getScore();
            log.info("updateRankProvince 活动:{} 省榜最大值:{} 最小值:{} ", activityId, m, n);
            rankBaseService.remove(key, userId);
            rankBaseService.addSingleRank(key, userId, qTotal);
            Double after = rankBaseService.getSingleRankScore(key, userId);
            log.info("updateRankProvince 结束更新用户:{},活动:{},省排行榜缓存:{}", userId, activityId, after);
        } catch (Exception e) {
            log.error("updateRankProvince 更新用户省排行榜缓存错误:{}", e);
        }
    }

    @Override
    public void deleteRankProvince(String activityId, String taskId, String userId) {
        String key = this.getAllRankProvinceKey(activityId, taskId, this.getUserProvince(userId, activityId));
        rankBaseService.remove(key, userId);
    }

    @Override
    public String getUserProvince(String userId, String activityId) {
        log.info(":>>> 开始获取用户:{} 活动:{} 所在省", userId, activityId);
        Query query = new Query().addCriteria(Criteria.where("userId").is(userId).and("activityId").is(activityId).and("joined").is(Boolean.TRUE));
        UserParticipateActivityInfo up = mongoTemplate.findOne(query, UserParticipateActivityInfo.class);
        if (Objects.isNull(up) || StringUtils.isBlank(up.getUniversityId())) {
            return null;
        }
        Query queryU = new Query().addCriteria(Criteria.where("id").is(up.getUniversityId()));
        UniversityInfo universityInfo = mongoTemplate.findOne(queryU, UniversityInfo.class);
        if (Objects.isNull(universityInfo)) {
            return null;
        }
        return universityInfo.getProvince();
    }

    private UserPO get(String userId, String platform) {
        if (Objects.isNull(platform) || StringUtils.isBlank(platform)) {
            platform = "wechat-h5";
        }
        UserPO userPO = this.getUserCacheFromRedis(userId, platform);
        return userPO;
    }

    private String getAllRankUserKey(String userId, String platform) {
        return String.format(ALL_RANK_USER_KEY, userId, platform);
    }

    private UserPO getUserCacheFromRedis(String userId, String platform) {
        String key = this.getAllRankUserKey(userId, platform);
        Object o = redisTemplate.opsForValue().get(key);
        if (Objects.isNull(o)) {
            UserPO userPO = visitorUserService.find(userId, platform);
            if (Objects.isNull(userPO)) {
                return null;
            }
            this.setUserCacheFromRedis(userId, platform, userPO);
            return userPO;
        }
        return JSON.parseObject((String) o, UserPO.class);
    }

    private void setUserCacheFromRedis(String userId, String platform, UserPO userPO) {
        String key = this.getAllRankUserKey(userId, platform);
        redisTemplate.opsForValue().set(key, JSON.toJSONString(userPO), 1, TimeUnit.HOURS);
    }

}
